
package core.filter.type;

import java.util.LinkedList;
import java.util.List;

import tool.Complex;
import tool.Pair;
import core.FilterFactory;
import core.filter.BaseFilter;

public class InverseCzebyszew extends BaseFilter{

	static {
		FilterFactory.registerFilterType(FilterTypes.INVERSECZEBYSZEW, new InverseCzebyszew());
	}
	
	private long n;
	private double[] angles;
	
	protected List<Complex> polesList;
	protected List<Complex>	zerosList;
 	
	protected double getEpsilon(){
		return 1./Math.sqrt(Math.pow(10,0.1*suppressionR)-1);
	}

	protected double calculateDiscriminationInv(){
		return (Math.pow(10, 0.1*suppressionR) -1)/(Math.pow(10, 0.1*suppressionP) -1);
	}
	protected double calculateSelectivityInv(){
		return frequencyR/frequencyP;
	}
	protected double arccosh(double x){
		return Math.log(x+Math.sqrt(Math.pow(x,2)-1));
	}
	private double arcsinh(double x) {
		return Math.log(x+Math.sqrt(Math.pow(x, 2)+1));
	}
	
	protected long calculateN(double discr,double selec){
		double d1 =  arccosh(discr);
		double d2 =  arccosh(selec);
		System.out.println("1: "+d1+" 2: "+d2);
		double n= (arccosh(Math.sqrt(discr))/(arccosh(selec)));
		long temp = Math.round(n);
		if(temp != n && 10*temp <= 10*n)
			return temp+1;
		return temp;
	}
	
	private void getAngles() {
		angles = new double[(int) n];
		for (int i=1;i<=n;i++) {
			angles[i-1] = (2*i-1)*Math.PI/(2*n);
		}
	}

	@Override
	public List<Pair> getTransmittance(List<Double> list) {
		
		double discr = calculateDiscriminationInv();
		double selec = calculateSelectivityInv();
		n = calculateN(discr, selec);
		
		System.out.println("d: "+discr+" s: "+selec+" n: "+n);
		System.out.println("E: "+getEpsilon());
		getAngles();
		getPolesList();
		getZerosList();
		List<Pair> result = new LinkedList<Pair>();
		System.out.println("Bieguny:");
		for(Complex comp : polesList) {
			System.out.println(comp.toString());
		}
		System.out.println("Zera:");
		for(Complex comp : zerosList) {
			System.out.println(comp.toString());
		}
		for(Double signal : list) {
			Complex H0 = getH0();
			System.out.println("H0 = "+H0);
			Complex comp = H0.times(getZerosMultiplication(signal/frequencyP)).division(getPolesMultiplication(signal/frequencyP));
			result.add(new Pair(signal, comp));
		}
		return result;
	}
	
	public Complex getPolesMultiplication(Double signal){
		Complex result = new Complex(1, 0);;
		if(signal != null) {
			for (Complex pole : polesList) {
				result = result.times(pole.times(-1).addComplex(new Complex(signal.doubleValue(), 0)));
			}
		}
		else {
			for (Complex pole : polesList) {
				result = result.times(pole);
			}
		}
		
		return result;
	}
	
	public Complex getZerosMultiplication(Double signal){
		Complex result = new Complex(1, 0);;
		if(signal != null) {
			for (Complex zero : zerosList) {
				result = result.times(zero.times(zero).addComplex(new Complex(signal.doubleValue(), 0)));
			}
		}
		else {
			for (Complex zero : zerosList) {
				zero = zero.times(zero);
				result = result.times(zero);
			}
		}
		
		return result;
	}
	
	@Override
	public List<Complex> getPolesList() {
		if(polesList == null){
			polesList = new LinkedList<Complex>();
			double epsilon = getEpsilon();
			double epsinv = 1/epsilon;
			double Fi = arcsinh(epsinv)/n;
			System.out.println("Fi: "+Fi);
			for(int i=0;i<n;i++) {
				double R1 = -1*Math.sinh(Fi);
				double R2 = Math.cosh(Fi);
				Complex com = new Complex(R1, 0).times(Math.sin(angles[i])).addComplex(new Complex(0, -1).times(R2).times(Math.cos(angles[i])));
				polesList.add(new Complex(1,0).division(com));
			}
		}
		return polesList;
	}
	
	public List<Complex> getZerosList(){
		if(zerosList == null){
			zerosList = new LinkedList<Complex>();
			int m = (int) n/2;
			for(int i=1;i<=m;i++) {
				double R = 1./Math.cos(angles[i-1]);
				Complex imag = new Complex(0,-1).times(R);
				zerosList.add(imag);
			}
		}
	return zerosList;	
	}
	
	private Complex getH0() {
		return getPolesMultiplication(null).division(getZerosMultiplication(null));
	}

}

